package middleware

import (
	"github.com/gin-gonic/gin"
	"github.com/spf13/cast"
	"seven-admin/app/admin/schemas/req"
	"seven-admin/app/admin/service/system"
	sysModel "seven-admin/app/collection/system"
	"seven-admin/core"
	"seven-admin/core/config"
	"seven-admin/core/response"
	"seven-admin/util"
	"strconv"
	"strings"
	"time"
)

var (
	permSrv       = system.NewAuthPermService(core.GetDB())
	roleSrv       = system.NewAuthRoleService(core.GetDB(), permSrv)
	adminSrv      = system.NewAuthAdminService(core.GetDB(), permSrv, roleSrv)
	tenantPermSrv = system.NewAuthTenantPermService(core.GetDB())
)

// TokenAuth Token认证中间件
func TokenAuth() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 路由转权限
		auths := strings.ReplaceAll(strings.Replace(c.Request.URL.Path, "/api/", "", 1), "/", ":")

		// 免登录接口
		if util.ToolsUtil.Contains(config.AdminConfig.NotLoginUri, auths) {
			c.Next()
			return
		}

		// Token是否为空
		token := c.Request.Header.Get("Token")
		if token == "" {
			response.Fail(c, response.TokenEmpty)
			c.Abort()
			return
		}
		// Token是否过期
		tokenKey := config.AdminConfig.BackstageTokenKey + token
		existCnt := util.RedisUtil.Exists(tokenKey)
		if existCnt < 0 {
			response.Fail(c, response.SystemError)
			c.Abort()
			return
		} else if existCnt == 0 {
			response.Fail(c, response.TokenInvalid)
			c.Abort()
			return
		}

		// 用户信息缓存
		uidStr := util.RedisUtil.Get(tokenKey)
		var uid, cid uint
		if uidStr != "" {
			spilt := strings.Split(uidStr, "_")
			uid = cast.ToUint(spilt[1])
			cid = cast.ToUint(spilt[0])
		}

		if !util.RedisUtil.HExists(config.AdminConfig.BackstageManageKey, uidStr) {
			err := adminSrv.CacheAdminUserByUid(uid)
			if err != nil {
				core.Logger.Errorf("TokenAuth CacheAdminUserByUid err: err=[%+v]", err)
				response.Fail(c, response.SystemError)
				c.Abort()
				return
			}
		}

		// 校验用户被删除
		var mapping sysModel.AuthAdmin
		err := util.ToolsUtil.JsonToObj(util.RedisUtil.HGet(config.AdminConfig.BackstageManageKey, uidStr), &mapping)
		if err != nil {
			core.Logger.Errorf("TokenAuth Unmarshal err: err=[%+v]", err)
			response.Fail(c, response.SystemError)
			c.Abort()
			return
		}

		// 校验用户被禁用
		if mapping.IsDisable == 1 {
			response.Fail(c, response.LoginDisableError)
			c.Abort()
			return
		}

		// 令牌剩余30分钟自动续签
		if util.RedisUtil.TTL(tokenKey) < 1800 {
			util.RedisUtil.Expire(tokenKey, 7200)
		}

		auth := req.AuthReq{
			Id:             uid,
			TenantId:       cid,
			IsSuperAdmin:   mapping.ID == config.AdminConfig.SuperAdminId,
			IsSuperTenant:  mapping.TenantId == config.AdminConfig.SuperTenantId,
			SuperAdminId:   config.AdminConfig.SuperAdminId,
			SuperTenantId:  config.AdminConfig.SuperTenantId,
			SoftSuperAdmin: mapping.RoleInfo != nil && mapping.RoleInfo.SoftSuperAdmin == 1,
			DeptId:         mapping.DeptId,
			PostId:         mapping.PostId,
			RoleId:         cast.ToUint(mapping.Role),
			Username:       mapping.Username,
			Nickname:       mapping.Nickname,
		}

		// 检查租户是否停用或过期
		now := time.Now().Unix()
		if !auth.IsSuperTenant && mapping.Tenant.ExpiredAt > 0 && (mapping.Tenant.IsStop == 1 || mapping.Tenant.ExpiredAt < now) {
			response.Fail(c, response.TenantDisableOrExpired)
			c.Abort()
			return
		}

		// 单次请求信息保存
		c.Set(config.AdminConfig.ReqAuthReq, &auth)
		c.Set(config.AdminConfig.ReqAdminIdKey, uid)

		// 免权限验证接口
		if util.ToolsUtil.Contains(config.AdminConfig.NotAuthUri, auths) || auth.IsSuperAdmin {
			c.Next()
			return
		}

		// 校验租户权限或角色权限
		var checkRolePermissions func(roleId string) bool
		checkRolePermissions = func(roleId string) bool {
			if util.RedisUtil.HExists(config.AdminConfig.BackstageRolesKey, roleId) {
				i := cast.ToUint(roleId)
				err = permSrv.CacheRoleMenusByRoleId(i)
				if err != nil {
					core.Logger.Errorf("TokenAuth CacheRoleMenusByRoleId err: err=[%+v]", err)
					response.Fail(c, response.SystemError)
					c.Abort()
					return false
				}

				menus := util.RedisUtil.HGet(config.AdminConfig.BackstageRolesKey, roleId)
				return menus != "" && util.ToolsUtil.Contains(strings.Split(menus, ","), auths)
			}
			return false
		}

		roleId := mapping.Role
		var hasPermission bool
		if auth.IsSuperTenant {	
			hasPermission = checkRolePermissions(roleId)
		} else {
			tenantId := strconv.Itoa(int(mapping.TenantId))
			if util.RedisUtil.HExists(config.AdminConfig.BackstageTenantsKey, tenantId) {
				err := tenantPermSrv.CacheTenantMenusByTenantId(mapping.TenantId)
				if err != nil {
					core.Logger.Errorf("TokenAuth CacheTenantMenusByTenantId err: err=[%+v]", err)
					response.Fail(c, response.SystemError)
					c.Abort()
					return
				}

				if auth.SoftSuperAdmin {
					menus := util.RedisUtil.HGet(config.AdminConfig.BackstageTenantsKey, tenantId)
					hasPermission = menus != "" && util.ToolsUtil.Contains(strings.Split(menus, ","), auths)
				} else {
					hasPermission = checkRolePermissions(roleId)
				}
			}
		}

		if !hasPermission {
			response.Fail(c, response.NoPermission)
			c.Abort()
			return
		}

		c.Next()
	}
}
